﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using MyFloridaHouse.libSvCommonUtilities;
using MyFloridaHouse.libSvOpenDistricts.Criteria;
using MyFloridaHouse.libSvOpenDistricts.Districts;
using MyFloridaHouse.libSvOpenDistricts.enumerations;
using MyFloridaHouse.ServiceConn.DistrBldrSvcRef;

namespace MyFloridaHouse.libSvOpenDistricts.SummaryDataGrid
{
    //TODO: Change SummaryDataGridManager to static class, like this: http://forums.silverlight.net/forums/p/194335/451173.aspx, also change regionfill, regionlabel, etc. to implement static class like link
    

    public delegate void SummaryDataGridDatasetCriteriaChangedEventHandler(EventArgs e);

    public partial class SummaryDataGridManager : INotifyPropertyChanged
    {

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        #region Events

        public static event EventHandler SummaryDataGridDatasetCriteriaChanged;

        public static void RaiseSummaryDataGridDatasetCriteriaChanged(object sender)
        {
            if (SummaryDataGridDatasetCriteriaChanged != null)
            {
                SummaryDataGridDatasetCriteriaChanged(sender, new EventArgs());
            }
        }

        #endregion

        #region properties

        // these ID's define the data we want to get from the database for each district
        private DataDictionaryIDList summaryDataGridDataIDs = new DataDictionaryIDList();

        public string activeLayerIDList
        {
            get { return summaryDataGridDataIDs.layerIDList; }
        }

        public string activeDictionaryIDList
        {
            get { return summaryDataGridDataIDs.dictionaryIDList; }
        }

        // this describes the final data to show
        private List<SummaryDataGridColumnDescription> _summaryDataGridColumnDescriptionList = new List<SummaryDataGridColumnDescription>();
        public List<SummaryDataGridColumnDescription> summaryDataGridColumnDescriptionList
        {
            get { return _summaryDataGridColumnDescriptionList; }
        }

        // used internally to keep the district row data as original
        private ObservableCollection<SummaryDataGridRow> _dataGridList = new ObservableCollection<SummaryDataGridRow>();

        // used for external sorted viewing
        private ObservableCollection<SummaryDataGridRow> _districtSummaryDataGridList = new ObservableCollection<SummaryDataGridRow>();
        public ObservableCollection<SummaryDataGridRow> districtSummaryDataGridList
        {
            get { return _districtSummaryDataGridList; }
            set
            {
                if (_districtSummaryDataGridList != value)
                {
                    _districtSummaryDataGridList = value;
                    NotifyPropertyChanged("districtSummaryDataGridList");
                }
            }
        }

        private enumDataGridMode _dataGridMode = enumDataGridMode.District;
        public enumDataGridMode dataGridMode
        {
            get { return _dataGridMode; }
            set
            {
                if (_dataGridMode != value)
                {
                    _dataGridMode = value;
                    UpdateDataGrid();
                    NotifyPropertyChanged("dataGridMode");
                }
            }
        }

        private int _workingPopulationDeviation = -99999;
        public int workingPopulationDeviation
        {
            get 
            {
                return _workingPopulationDeviation;
            }
            set
            {
                if (_workingPopulationDeviation != value)
                {
                    _workingPopulationDeviation = value;

                    NotifyPropertyChanged("workingPopulationDeviation");
                }
            }
        }
        
        #endregion

        private int _totalPopulation = Constants.TOTAL_POPULATION_TO_BE_APPORTIONED;

        private string _criteriaDatasetName;
        public string criteriaDatasetName 
        { 
            get 
            { 
                return _criteriaDatasetName; 
            }
            set 
            {
                _criteriaDatasetName = value;
                NotifyPropertyChanged("criteriaDatasetName"); 
            } 
        }

        private int _districtCount = 0;
        private int _dataGridTotalPopluationIndex;  //initialized when criteria changes

        public SummaryDataGridManager()
        {
            LoadCriteriaDataset(Constants.DefaultCriteriaDatasetName);
            IntializeCommands();
        }

        public void UpdateTotalPopulation(int newTotalPopulation)
        {
            _totalPopulation = newTotalPopulation;
        }

        public void UpdateWorkingPopulationDeviation()
        {

            int workingDeviation = 0;
            var districtValuesQuery = from dataGridRow in _dataGridList
                                      where dataGridRow.districtID != 0
                                      select dataGridRow.valuesList;
            foreach (var districtDataGridValues in districtValuesQuery)
            {
                try
                {
                    // ignore a district if it doesn't have any population
                    int districtPopulation = districtDataGridValues[_dataGridTotalPopluationIndex];
                    if (districtPopulation > 0)
                    {
                        workingDeviation += districtDataGridValues[1];
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Unable to calculate total population deviation for the datagrid");
                    workingDeviation = -99990;
                    break;
                }
            }
            workingPopulationDeviation = workingDeviation;
        }
    

        public void LoadCriteriaDataset(string newCriteriaDatasetName)
        {
            if (criteriaDatasetName != newCriteriaDatasetName)
            {
                criteriaDatasetName = newCriteriaDatasetName;
                BuildSummaryDataGridColumnDescriptions(criteriaDatasetName);
                summaryDataGridDataIDs = CriteriaDataset.GetUniqueDictionaryIDListFromDatasetName(criteriaDatasetName);
                RaiseSummaryDataGridDatasetCriteriaChanged(this);
            }
        }


        public void Reset(int districtCount, ObservableCollection<District> districtList)
        {
            _districtCount = districtCount;

            if (districtCount > 0)
            {

                _dataGridList.Clear();

                for (int districtID = 0; districtID <= districtCount; districtID++)
                {
                    ObservableCollection<int> valuesList = new ObservableCollection<int>();

                    // for each district, loop through all the columns to find the target population column
                    int ndx = 0;
                    foreach (var col in _summaryDataGridColumnDescriptionList)
                    {
                        if (ndx == 0)
                        {
                            // override the district id
                            valuesList.Add(districtID);
                        }
                        else if (ndx == 1)
                        {
                            // override the target population column
                            valuesList.Add((-districtList[districtID].targetPopulation));
                        }
                        else
                        {
                            valuesList.Add(0);
                        }
                        ndx++;
                    }


                    SummaryDataGridRow newRow = new SummaryDataGridRow();
                    newRow.valuesList = valuesList;
                    newRow.districtID = districtID;
                    _dataGridList.Add(newRow);
                }
                UpdateDataGrid();

            }
        }

        private static bool IsDataGridColumnTotalPopulation(SummaryDataGridColumnDescription col)
        {
            bool result = false;
            if (col != null)
            {
                // NOTE: We are looking in our column description list
                // if criteria is null, it's reserved for either districtID or population deviation,
                // therefore, it won't be in the list of data coming back from the database, 
                // so don't increment our index of the database result list
                if (col.critera != null)
                {
                    if (col.critera.dictionaryList != null)
                    {
                        if (col.critera.dictionaryList.Count == 1)
                        {
                            if( (col.critera.dictionaryList[0].dictionaryID == Constants.PopulationDictionaryID)
                               & (col.critera.dictionaryList[0].layerID == Constants.PopulationDictionaryIDSourceLayerID))
                            {
                                result = true;
                            }
                        }
                    }
                }
            }
            return result;
        }


        #region Use criteria to identify columns to build

        private void BuildSummaryDataGridColumnDescriptions(string criteriaDatasetName)
        {
            ObservableCollection<CriteriaDatasetDTO> criteriaList = CriteriaDatasetManager.GetCriteriaDataset(criteriaDatasetName);

            _summaryDataGridColumnDescriptionList.Clear();
            _summaryDataGridColumnDescriptionList.Add(BuildSummaryDataGridColumn("DistrictID", "District ID", "District Number", true, null));
            _summaryDataGridColumnDescriptionList.Add(BuildSummaryDataGridColumn("PopDev", "Pop Dev", "Population deviation from the ideal", true, null));
            

            foreach (var criteria in criteriaList)
            {
                _summaryDataGridColumnDescriptionList.Add(BuildSummaryDataGridColumn(criteria.criteriaName, criteria.criteriaHeader, criteria.description, false, criteria));
            }


            bool foundPopulationIndex = FindPopulationColumnIndex(summaryDataGridColumnDescriptionList, out _dataGridTotalPopluationIndex);
            if (!foundPopulationIndex)
            {
                MessageBox.Show("The Summary Grid criteria you specified doesn't have Census Total Population in it.  District deviation from desired population will not work.");
                return;
            }

        }


        private static bool FindPopulationColumnIndex(List<SummaryDataGridColumnDescription> columnList, out int populationColumnIndex)
        {
            populationColumnIndex = -1;
            bool foundPopulationIndex = false;
            foreach (var col in columnList)
            {
                populationColumnIndex++;
                if (IsDataGridColumnTotalPopulation(col))
                {
                    foundPopulationIndex = true;
                    break;
                }
            }
            return foundPopulationIndex;
        }

        private static SummaryDataGridColumnDescription BuildSummaryDataGridColumn(string name, string header, string description, bool isFrozen, CriteriaDatasetDTO criteria)
        {
            SummaryDataGridColumnDescription newDescr = new SummaryDataGridColumnDescription();
            newDescr.name = name;
            newDescr.header = header;
            newDescr.description = description;
            newDescr.isFrozen = isFrozen;
            newDescr.critera = criteria;
            return newDescr;
        }
        #endregion

        private static int GetDistrictTotalPopulation(ObservableCollection<RegionDataDTO> districtDataSummaryList)
        {
            int Tpop = 0;
            var districtPopulationQuery = from districtDataSummaryValues in districtDataSummaryList
                                          where districtDataSummaryValues.dictionaryID == Constants.PopulationDictionaryID
                                              & districtDataSummaryValues.sourceLayerID == Constants.PopulationDictionaryIDSourceLayerID
                                          select districtDataSummaryValues.dataValue;
            if (districtPopulationQuery.Count() > 0)
            {
                string tpopDataValue = districtPopulationQuery.First();
                Int32.TryParse(tpopDataValue, out Tpop);
            }
            else
            {
                Tpop = 0;
            }
            return Tpop;
        }

        public void ReplaceSummaryDataGridDataRow(int districtID, int targetPopulation, ObservableCollection<RegionDataDTO> districtDataSummaryList)
        {
            SummaryDataGridRow newRow = new SummaryDataGridRow();
            newRow.valuesList = new ObservableCollection<int>();
            newRow.districtID = districtID;
            newRow.lastChangedTimeStamp = DateTime.Now;

            if (districtID >= _dataGridList.Count())
            {
                MessageBox.Show("Unable to display district data summary,No districts defined for data grid");
                return;
            }

            bool noDataReceived = false;
            if (districtDataSummaryList == null)
                noDataReceived = true;
            else if (districtDataSummaryList.Count() == 0)
                noDataReceived = true;

            if (noDataReceived)
            {
                // got no data to display so just fill it with blanks
                _dataGridList[districtID].valuesList.Clear();
                foreach (var col in summaryDataGridColumnDescriptionList)
                {
                    _dataGridList[districtID].valuesList.Add(0);
                }

                _dataGridList[districtID].valuesList[0] = districtID;
                _dataGridList[districtID].valuesList[1] = -targetPopulation;
                return;
            } 
                
            // Population Deviation
            int Tpop = GetDistrictTotalPopulation(districtDataSummaryList);
            int TpopDev = Tpop - targetPopulation;

            // add the special columns for data grid
            newRow.valuesList.Add(districtID); // districtID
            newRow.valuesList.Add(TpopDev);

            foreach (var col in summaryDataGridColumnDescriptionList)
            {
                int regionComputedDataValue = 0;

                if (col.critera != null)
                {
                    //CriteriaDataset.EvaluateCriteria(districtDataSummaryList[districtID].ToList(), col.critera, out regionComputedDataValue);
                    CriteriaDataset.EvaluateCriteria(districtDataSummaryList.ToList(), col.critera, false, out regionComputedDataValue);
                    newRow.valuesList.Add(regionComputedDataValue);
                }
            }

            _dataGridList[districtID] = newRow;

            // remember, this list doesn't have district 0, so adjust for it
            districtSummaryDataGridList[districtID-1] = newRow;

            UpdateWorkingPopulationDeviation();
        }

        public void UpdateDataGrid()
        {
            IOrderedEnumerable<SummaryDataGridRow> dataValueQuery;

            switch (_dataGridMode)
            {
                case enumDataGridMode.County:

                case enumDataGridMode.Changed:
                    dataValueQuery = from dataGridRow in _dataGridList
                                     where dataGridRow.districtID != 0
                                     orderby dataGridRow.lastChangedTimeStamp descending
                                     select dataGridRow;

                    break;

                default:
                    //case enumDataGridMode.District:
                    dataValueQuery = from dataGridRow in _dataGridList
                                     where dataGridRow.districtID != 0
                                     orderby dataGridRow.districtID ascending
                                     select dataGridRow;
                    break;
            }

            districtSummaryDataGridList.Clear();
            foreach (var districtDataSummaryRow in dataValueQuery)
            {
                if (districtDataSummaryRow.districtID != 0)
                {
                    districtSummaryDataGridList.Add(districtDataSummaryRow);
                }
            }

        }


    }

}

